/* 00-prod-cons.c

   All 'W' objects wait for a single 'N' object to complete.  If the user
   specifies that exactly one of the 'W' objects wakes up, one does and 
   finishes leaving the others in limbo.  Otherwise, all 'W' objects wake up
   and finish.

   Usage: 00-prod-cons [ signal | broadcast ]

   Note: the 'Monitor' in this case consists of procedures a and b
*/
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

bool flag;
   
pthread_mutex_t monitor_mutex;
pthread_cond_t monitor_wait;

/* 
   Exactly one active thread is allowed to run in this function at a time.
   When a thread is put into a wait state with pthread_cond_wait it gives
   up its monitor_mutex lock so that another thread may enter the function.
   When a waiting thread is signaled with pthread_cond_signal or
   pthread_cond_broadcast it becomes active and no other thread will be 
   active in the function until it leaves.
*/
void a (void *data) {
   pthread_mutex_lock(&monitor_mutex);
   printf("\ta: wait W(%d)\n", (long)data);
   pthread_cond_wait(&monitor_wait, &monitor_mutex); 
   printf("\ta: rise W(%d)\n", (long)data);
   pthread_mutex_unlock(&monitor_mutex);
}

void b (void *data) {
   pthread_mutex_lock(&monitor_mutex);
   printf("\tb: notify N(%d)\n", (long)data);
   if (flag) 
      pthread_cond_signal(&monitor_wait);
   else
      pthread_cond_broadcast(&monitor_wait);
   printf("\tb: done N(%d)\n", (long)data);
   pthread_mutex_unlock(&monitor_mutex);
}

/*
  This is the function that is tied to the 'W' objects
*/
void *wrun (void *data) {
   printf("Starting  W(%d)\n", (long)data);
   a(data);
   printf("Finishing W(%d)\n", (long)data);
   pthread_exit(NULL);
}

/*
  This is the function that is tied to the 'N' objects
*/
void *nrun (void *data) {
   printf("Starting  N(%d)\n", (long)data);
   b(data);
   printf("Finishing N(%d)\n", (long)data);
   pthread_exit(NULL);
}

int main (int argc, char **argv) {
   pthread_t w1,w2,w3,w4,w5,w6,n1;
   pthread_attr_t attr;
   long t1=1, t2=2, t3=3, t4=4, t5=5, t6=6, t7=1;

   if (argc != 2) {
      printf("Usage: 01-prod-cons [signal | broacdcast]\n");
      exit(0);
   }

   /* initialize the mutex and cond variables */   
   pthread_mutex_init(&monitor_mutex, NULL);
   pthread_cond_init (&monitor_wait, NULL);

   flag = (!strncmp(argv[1],"signal",6)) ? true : false;
   
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
   pthread_create(&w1, &attr, wrun, (void*)t1);
   pthread_create(&w2, &attr, wrun, (void*)t2);
   pthread_create(&w3, &attr, wrun, (void*)t3);
   pthread_create(&w4, &attr, wrun, (void*)t4);
   pthread_create(&w5, &attr, wrun, (void*)t5);
   pthread_create(&w6, &attr, wrun, (void*)t6);
   pthread_create(&n1, &attr, nrun, (void*)t7);

   pthread_exit(NULL);
}
